package com.fubowen.lepton.dao

import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Embedded
import androidx.room.Insert
import androidx.room.MapInfo
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Relation
import androidx.room.Transaction
import androidx.room.Update
import com.fubowen.lepton.bean.Theme
import com.fubowen.lepton.bean.ThemeValue
import kotlinx.coroutines.flow.Flow

@Dao
interface ThemeDao {
    @Query("select * from Theme")
    fun all(): List<Theme>

    @Query("select * from Theme where Id = :id")
    fun getById(id: Int): Theme?

    @Query("select * from Theme where isUsed = 1")
    fun getUsed(): Theme?

    @Query("select * from Theme where isUsed = 1")
    fun getUsedFlow(): Flow<Theme?>

    @Query("select * from Theme where name = :name limit 1")
    fun getByName(name: String): Theme?

    @MapInfo(keyColumn = "name", valueTable = "Theme")
    @Query("select * from Theme a  where name in (:names)")
    fun getByName(names: List<String>): Map<String, Theme>

    @Insert
    fun set(bean: Theme): Long

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun setOrReplace(bean: Theme)

    @Update
    fun put(bean: Theme)

    @Query("UPDATE Theme set isUsed=:isUsed where id=:themeId")
    fun putIsUsed(themeId: Int, isUsed: Boolean)

    @Query("UPDATE Theme set isUsed=0 where isUsed=1")
    fun putIsUsedToFalse()

    @Delete
    fun del(bean: Theme)

    @Query("DELETE FROM Theme")
    fun clear()

    //region ThemeValue

    @Query("SELECT Theme.*,ThemeValue.* FROM Theme LEFT JOIN ThemeValue ON Theme.id = ThemeValue.themeId where Theme.id = :themeId")
    fun getThemeById(themeId: Int): ThemeWithValue?

    fun getThemeByIdToInherit(themeId: Int): ThemeWithValueResult? {
        var id = themeId
        var theme: ThemeWithValue?
        val themeList = mutableListOf<ThemeWithValue>()
        do {
            theme = getThemeById(id)
            if (theme == null) {
                break
            }
            themeList.add(theme)
            id = theme.theme.parentId
        } while (id != -1)

        if (themeList.isEmpty()) {
            return null
        }
        val init = mutableMapOf<String, ThemeValue>()
        themeList.reversed().fold(init) { curr, next ->
            next.themeValueList.forEach {
                curr[it.key] = it
            }
            curr
        }
        return ThemeWithValueResult(
            theme = themeList.last().theme,
            themeValueList = init
        )
    }

    fun setUsedTheme(themeId: Int) {
        val theme = getUsed()
        if (theme != null) {
            if (theme.id != themeId) {
                theme.isUsed = false
                put(theme)
            }
        } else {
            putIsUsed(themeId, true)
        }
    }

    fun getUsedTheme(): ThemeWithValueResult? {
        val theme = getUsed()
        if (theme != null) {
            return getThemeByIdToInherit(theme.id)
        }
        return null
    }

    fun getThemeByNameToInherit(name: String): ThemeWithValueResult? {
        val theme = getByName(name)
        if (theme != null) {
            return getThemeByIdToInherit(theme.id)
        }
        return null
    }

    @Query("DELETE FROM ThemeValue where themeId = :themeId")
    fun deleteThemeValueByThemeId(themeId: Int)

    @Insert
    fun setThemeValue(beans: List<ThemeValue>)

    @Transaction
    fun putTheme(beans: List<ThemeWithValueResult>) {
        beans.forEach { item ->
            if (item.theme.isUsed) {
                putIsUsedToFalse()
            }
            put(item.theme)
            item.themeValueList.values.forEach { it.themeId = item.theme.id }
            deleteThemeValueByThemeId(item.theme.id)
            setThemeValue(item.themeValueList.values.toList())
        }
    }

    @Transaction
    fun setTheme(beans: List<ThemeWithValueResult>) {

        beans.forEach { item ->
            if (item.theme.isUsed) {
                putIsUsedToFalse()
            }
            val id = set(item.theme).toInt()
            item.themeValueList.values.forEach { it.themeId = id }
            setThemeValue(item.themeValueList.values.toList())
        }
    }

    //endregion
}

data class ThemeWithValue(
    @Embedded
    val theme: Theme,
    @Relation(parentColumn = "id", entityColumn = "themeId")
    val themeValueList: List<ThemeValue>
)

class ThemeWithValueResult(
    var theme: Theme,
    var themeValueList: Map<String, ThemeValue>
)